﻿using System;
using System.Collections.Generic;
using System.Linq;
using Boxi.Extensions;
using Boxi.ViewModel;

namespace Boxi.Model
{
    public struct GameModel
    {
        #region Properties
        public Byte Columns
        {
            get
            {
                return _columns;
            }
        }
        private Byte _columns;
        public Byte Rows
        {
            get
            {
                return _rows;
            }
        }
        private Byte _rows;
        
        #endregion
        #region Constructors
        public GameModel(Byte columns, Byte rows)
        {
            _columns = columns;
            _rows = rows;
        }
        #endregion
        #region Methods
        /// <summary>
        /// Warning!!! Does not make copy
        /// </summary>
        /// <param name="playerID"></param>
        /// <param name="moveOptions"></param>
        /// <param name="moveHistories"></param>
        /// <param name="game"></param>
        /// <returns></returns>
        public static MoveModel[] GetMovesConnectedThatWouldScore(MoveOptionList moveOptions, MoveHistoryList moveHistories, SByte playerID, GameModel game, ref Byte score, Byte moveID)
        {
            if (moveID == 0)
                throw new Exception("moveID can not be  0");

            List<MoveModel> movesThatWouldScore = new List<MoveModel>();

            Byte originalScore = score;
            score += (Byte)moveOptions.GetPointsThatWillScore(moveID, game).Length;
            moveOptions.SetMove(moveHistories, moveID, playerID, game);
            movesThatWouldScore.Add(moveOptions[moveID - 1]);

            if (score > originalScore)
            {
                MoveModel[] availableRelatedMoves = moveOptions.GetOpenOffensiveMoves(game).Where(m => (m.FirstPoint != 0 && (new PointModel(m.FirstPoint, game.Columns)).Contains(moveID)) || (m.SecondPoint != 0 && (new PointModel(m.SecondPoint, game.Columns)).Contains(moveID))).ToArray();
                if (availableRelatedMoves.Length > 1)
                    throw new Exception("This should never happen");
                else if (availableRelatedMoves.Length == 1)
                    GetMovesConnectedThatWouldScore(moveOptions.Copy(), moveHistories, playerID, game, ref score, availableRelatedMoves[0].MoveID).ToList().ForEach(m => movesThatWouldScore.Add(m));
            }
            return movesThatWouldScore.ToArray();
        }
        /// <summary>
        /// Warning!!! Does not make copy
        /// </summary>
        /// <param name="playerID"></param>
        /// <param name="moveOptions"></param>
        /// <param name="moveHistories"></param>
        /// <param name="game"></param>
        /// <returns></returns>
        public static SByte PredictWhoWillScoreMore(MoveOptionList moveOptions, MoveHistoryList moveHistories, SByte[] players, SByte playerID, GameModel game, Int32 moves)
        {
            SByte currentPlayerID = playerID;

            Byte[] playerScoreChange = new Byte[players.Length];

            for (Byte i = 0; i < moves; i++)
            {
                Byte score = 0;
                SetAllScoringMoves(moveOptions, moveHistories, playerID, game, ref score);
                playerScoreChange[currentPlayerID] += score;
                MoveModel[] movesThatDoNotLetNextPlayerScore = moveOptions.GetOpenDefensiveMoves(game);
                if (movesThatDoNotLetNextPlayerScore.Length > 0)
                {
                    moveOptions.SetMove(moveHistories, movesThatDoNotLetNextPlayerScore.GetRandom().MoveID, currentPlayerID, game);
                }
                else
                {
                    MoveModel[] openMoves = moveOptions.GetOpenMoves();
                    if (openMoves.Length > 0)
                    {
                        moveOptions.SetMove(moveHistories, openMoves.GetRandom().MoveID, currentPlayerID, game);
                    }
                    else
                    {
                        break;
                    }
                }
                currentPlayerID = players.GetNextPlayer(currentPlayerID);
            }
            SByte highestScorePlayer = 0;
            Byte highestScore = 0;
            for (Byte i = 0; i < playerScoreChange.Length; i++)
            {
                if (playerScoreChange[i] > highestScore || (playerScoreChange[i] == highestScore && players[i] == playerID))
                {
                    highestScore = playerScoreChange[i];
                    highestScorePlayer = players[i];
                }
            }

            if (highestScorePlayer == 0)
                throw new Exception("highestScorePlayer IS 0");

            return highestScorePlayer;
        }
        /// <summary>
        ///  Makes Copy
        /// </summary>
        /// <param name="playerID"></param>
        /// <param name="moveOptions"></param>
        /// <param name="moveHistories"></param>
        /// <param name="game"></param>
        /// <returns></returns>
        public static MoveModel GetMostDefensiveMove(MoveOptionList moveOptions, SByte playerID, MoveHistoryList moveHistories, GameModel game)
        {
            //Copy MoveData
            MoveModel bestMove = default(MoveModel);
            Byte lowestScore = 255;
            MoveModel[] defensiveMoves = moveOptions.GetOpenBadMoves(game);
            for (Byte i = 0; i < defensiveMoves.Length; i++)
            {
                MoveHistoryList moveHistoriesCopy = moveHistories.Copy();
                MoveOptionList moveOptionCopy = moveOptions.Copy();
                Byte score = 0;
                moveOptionCopy.SetMove(moveHistoriesCopy, defensiveMoves[i].MoveID, playerID, game);
                SetAllScoringMoves(moveOptionCopy, moveHistoriesCopy, playerID, game, ref score);
                if (score < lowestScore)
                {
                    lowestScore = score;
                    bestMove = defensiveMoves[i];
                }
            }
            return bestMove;
        }
        /// <summary>
        /// Warning!!! Does not make copy
        /// </summary>
        /// <param name="playerID"></param>
        /// <param name="moveOptions"></param>
        /// <param name="moveHistories"></param>
        /// <param name="game"></param>
        /// <returns></returns>
        public static void SetAllScoringMoves(MoveOptionList moveOptions, MoveHistoryList moveHistories, SByte playerID, GameModel game, ref Byte score)
        {
            Boolean scored = false;
            MoveModel[] availableMoves = moveOptions.GetOpenOffensiveMoves(game);
            for (Byte i = 0; i < availableMoves.Length; i++)
            {
                score += (Byte)moveOptions.GetPointsThatWillScore(availableMoves[i].MoveID, game).Length;
                moveOptions.SetMove(moveHistories, availableMoves[i].MoveID, playerID, game);
                scored = true;
            }
            if (scored)
                SetAllScoringMoves(moveOptions, moveHistories, playerID, game, ref score);
        }
        /// <summary>
        /// Makes Copy
        /// </summary>
        /// <param name="moveID"></param>
        /// <param name="playerID"></param>
        /// <param name="moveOptions"></param>
        /// <param name="moveHistories"></param>
        /// <param name="game"></param>
        /// <returns></returns>
        public static MoveModel GetMostOffensiveMove(MoveOptionList moveOptions, MoveHistoryList moveHistories, SByte[] playerIDs, SByte playerID, GameModel game)
        {
            MoveModel mostOffensiveMove = default(MoveModel);
            MoveModel[] offensiveMoves = moveOptions.GetOpenOffensiveMoves(game);
            for (Byte i = 0; i < offensiveMoves.Length; i++)
            {
                Byte score = 0;
                MoveModel[] movesConnectedThatWouldScore = GetMovesConnectedThatWouldScore(moveOptions.Copy(), moveHistories.Copy(), playerID, game, ref score, offensiveMoves[i].MoveID);
                if (movesConnectedThatWouldScore.Length == 2 && playerID != PredictWhoWillScoreMore(moveOptions.Copy(), moveHistories.Copy(), playerIDs, playerID, game, playerIDs.Length))
                {
                    MoveModel[] throwOffMoves = movesConnectedThatWouldScore.Where(m => moveOptions[m.MoveID].MoveMadeOrder == 0 && moveOptions.IsNotDefensiveMove(m, game)).ToArray();
                    if (throwOffMoves.Length > 0)
                        mostOffensiveMove = throwOffMoves.GetRandom();
                }
                else
                {
                    return offensiveMoves.GetRandom();
                }
            }
            return mostOffensiveMove;
        }
        public static MoveModel GetComputerMove(MoveOptionList moveOptions, MoveHistoryList moveHistories, SByte[] playerIDs, SByte currentPlayerID, GameView.DifficultyType difficulty, GameModel game)
        {
            if (difficulty == GameView.DifficultyType.Easy)
            {
                MoveModel[] movesThatScore = moveOptions.GetOpenOffensiveMoves(game);
                if (movesThatScore.Length > 0)
                    return movesThatScore.GetRandom();

                MoveModel[] movesThatDoNotLetNextPlayerScore = moveOptions.GetOpenDefensiveMoves(game);
                if (movesThatDoNotLetNextPlayerScore.Length > 0)
                    return movesThatDoNotLetNextPlayerScore.GetRandom();

                return moveOptions.GetOpenMoves().GetRandom();
            }
            else if (difficulty == GameView.DifficultyType.Moderate)
            {
                MoveModel[] movesThatScore = moveOptions.GetOpenOffensiveMoves(game);
                if (movesThatScore.Length > 0)
                    return movesThatScore.GetRandom();


                MoveModel[] movesThatDoNotLetNextPlayerScore = moveOptions.GetOpenDefensiveMoves(game);
                if (movesThatDoNotLetNextPlayerScore.Length > 0)
                    return movesThatDoNotLetNextPlayerScore.GetRandom();

                MoveModel move = GetMostDefensiveMove(moveOptions, currentPlayerID, moveHistories, game);
                if (move.MoveID != 0)
                    return move;

                return moveOptions.GetOpenMoves().GetRandom();
            }
            else
            {
                MoveModel mostOffensiveMove = GetMostOffensiveMove(moveOptions, moveHistories, playerIDs, currentPlayerID, game);
                if (mostOffensiveMove.MoveID != 0)
                    return mostOffensiveMove;

                MoveModel[] movesThatDoNotLetNextPlayerScore = moveOptions.GetOpenDefensiveMoves(game);
                if (movesThatDoNotLetNextPlayerScore.Length > 0)
                    return movesThatDoNotLetNextPlayerScore.GetRandom();

                MoveModel move = GetMostDefensiveMove(moveOptions, currentPlayerID, moveHistories, game);
                if (move.MoveID != 0)
                    return move;

                return moveOptions.GetOpenMoves().GetRandom();
            }

        
        }
        public override String ToString()
        {
            return String.Format("{0},{1}", Columns, Rows);
        }
        #endregion 
    }
}
